package com.graphhopper.matching;

import com.graphhopper.GraphHopper;
import com.graphhopper.config.Profile;
import com.graphhopper.util.PMap;
import com.graphhopper.util.shapes.GHPoint;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.sql.Array;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public class Trajmatch {

    public static void main(String[] args) {
        String relDir = args.length == 1 ? args[0] : "";
        String strosm = "./sichuan.osm.pbf";
        String hdfsosm = "hdfs://127.0.0.1:9000/target/sichuan.osm.pbf";
        GraphHopper hopper = createGraphHopperInstance(relDir + strosm);
        generateTrajData(hopper, null);
        //routing(hopper);
        /*speedModeVersusFlexibleMode(hopper);
        headingAndAlternativeRoute(hopper);
        customizableRouting(relDir + "./sichuan.osm.pbf");*/

        // release resources to properly shutdown or start a new instance
        hopper.close();
    }

    static GraphHopper createGraphHopperInstance(String ghLoc) {
        GraphHopper hopper = new GraphHopper();
        hopper.setOSMFile(ghLoc);
        // specify where to store graphhopper files
        String strtarget = "target/routing-graph-cache";
        String hdfshopper = "hdfs://127.0.0.1:9000/hopper";
        hopper.setGraphHopperLocation(strtarget);
        hopper.setStoreOnFlush(false);

        // see docs/core/profiles.md to learn more about profiles
        hopper.setProfiles(new Profile("car").
                setVehicle("car").
                setWeighting("fastest").
                setTurnCosts(false));

        // this enables speed mode for the profile we called car
        //hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car"));
        //hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car"));

        // now this can take minutes if it imports or a few seconds for loading of course this is dependent on the area you import
        hopper.importOrLoad();
        return hopper;
    }

    public static class InputFormatMy {
        /**
         * Converts a request message into a list of {@link Observation} objects as input for map
         * matching.
         *
         * @param input JSON input format of sample data.
         * @return List of {@link Observation} objects.
         */
        public static List<Observation> format(String input) {
            List<Observation> samples = new LinkedList<>();

            try {
                Object jsoninput = new JSONTokener(input).nextValue();
                JSONArray jsonsamples = null;

                if (jsoninput instanceof JSONObject) {
                    jsonsamples = ((JSONObject) jsoninput).getJSONArray("request");
                }
                else {
                    jsonsamples = ((JSONArray) jsoninput);
                }

                Set<Long> times = new HashSet<>();
                for (int i = 0; i < jsonsamples.length(); ++i) {
                    Observation sample = new Observation(GHPoint.fromStringLonLat(
                            jsonsamples.getString(i)));
                    samples.add(sample);
                }
            }
            catch (JSONException e) {
                e.printStackTrace();
                throw new RuntimeException("parsing JSON request: " + e.getMessage());
            }

            return samples;
        }

        public static ArrayList<Observation> formatArrayList(String input) {
            ArrayList<Observation> samples = new ArrayList<>();

            try {
                Object jsoninput = new JSONTokener(input).nextValue();
                JSONArray jsonsamples = null;

                if (jsoninput instanceof JSONObject) {
                    jsonsamples = ((JSONObject) jsoninput).getJSONArray("request");
                }
                else {
                    jsonsamples = ((JSONArray) jsoninput);
                }

                Set<Long> times = new HashSet<>();

                for (int i = 0; i < jsonsamples.length(); ++i) {
                    JSONObject json_line = (JSONObject)jsonsamples.get(i);
                    String wkt = json_line.getString("point").replace(" ", ",");
                    String xy = wkt.substring(wkt.indexOf("(")+1,wkt.indexOf(")"));
                    Observation sample = new Observation(GHPoint.fromStringLonLat(xy));
                    samples.add(sample);
                }
            }
            catch (JSONException e) {
                e.printStackTrace();
                throw new RuntimeException("parsing JSON request: " + e.getMessage());
            }

            return samples;
        }

        public static ArrayList<Observation> formatFile(String filename) {
            String encoding = "UTF-8";
            File file = new File(filename);
            Long filelength = file.length();
            byte[] filecontent = new byte[filelength.intValue()];
            try {
                FileInputStream in = new FileInputStream(file);
                in.read(filecontent);
                in.close();
            }
            catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            try {
                return formatArrayList(new String(filecontent, encoding));
            }
            catch (UnsupportedEncodingException e) {
                System.err.println("The OS does not support " + encoding);
                e.printStackTrace();
                return null;
            }
        }

        public static ArrayList formattxtFile(String filename) {
            try {
                String temp = null;
                File f = new File(filename);
                InputStreamReader read = new InputStreamReader(new FileInputStream(f));
                ArrayList readList = new ArrayList();

                BufferedReader reader = new BufferedReader(read);
                while ((temp = reader.readLine()) != null && !"".equals(temp)) {
                    readList.add(temp);
                }
                read.close();
                return readList;
            }
            catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        //Clean duplicated in short time
        public static ArrayList<String> trajClean(ArrayList arrayList) {
            ArrayList<String> clean = new ArrayList<>();
            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            String[] temp = null;

            HashMap<String, String> distinct_map = new HashMap<>();

            try {
                for (int i = 0; i < arrayList.size(); i++) {
                    temp = ((String)arrayList.get(i)).split(",");

                    long round_interval = (df.parse(temp[2+3]).getTime() / 1000 / utils.Interval_Seconds);

                    String str_key = temp[0+3] + "," + temp[1+3] + "," + round_interval;

                    if (!distinct_map.containsKey(str_key)) {
                        distinct_map.put(str_key, (String)arrayList.get(i));
                        clean.add((String)arrayList.get(i));
                    }
                    else {
                        System.out.println("clean " + arrayList.get(i));
                    }
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                return null;
            }

            return clean;
        }

        public static ArrayList<String> trajClean_dis(ArrayList arrayList) {
            ArrayList<String> clean = new ArrayList<>();
            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            String[] temp = null;
            String[] tempn1 = null;

            ArrayList<Double> diff_seq = new ArrayList<>();

            try {
                for (int i = 1; i < arrayList.size(); i++) {
                    temp = ((String)arrayList.get(i)).split(",");
                    tempn1 = ((String)arrayList.get(i - 1)).split(",");

                    int diff = (int) ((df.parse(temp[2]).getTime() - df.parse(tempn1[2]).getTime()) / 1000);

                    double dis = utils.getDistance(Double.parseDouble(temp[0]), Double.parseDouble(temp[1]),
                            Double.parseDouble(tempn1[0]), Double.parseDouble(tempn1[1]));

                    diff_seq.add(dis);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                return null;
            }

            clean.add((String)arrayList.get(0));
            for (int i = 0; i < diff_seq.size(); i++) {
                if (diff_seq.get(i) > 0.00001) {
                    clean.add((String)arrayList.get(i+1));
                }
            }

            return clean;
        }

        //Filter abnormal speed
        public static ArrayList<String> trajFilter(ArrayList arrayList) {
            ArrayList<String> filter = new ArrayList<>();
            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            String[] temp = null;
            String[] tempn1 = null;

            ArrayList<Double> diff_seq = new ArrayList<>();

            try {
                for (int i = 1; i < arrayList.size(); i++) {
                    temp = ((String)arrayList.get(i)).split(",");
                    tempn1 = ((String)arrayList.get(i - 1)).split(",");

                    double diff = (double) ((df.parse(temp[2]).getTime() - df.parse(tempn1[2]).getTime()) / 1000);

                    double dis = utils.getDistance(Double.parseDouble(temp[0]), Double.parseDouble(temp[1]),
                            Double.parseDouble(tempn1[0]), Double.parseDouble(tempn1[1]));

                    diff_seq.add(dis);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                return null;
            }

            filter.add((String)arrayList.get(0));
            for (int i = 0; i < diff_seq.size(); i++) {
                //if (diff_seq.get(i) > 0 && diff_seq.get(i) <= Speed_Threshold) {
                if (diff_seq.get(i) > 0) {
                    filter.add((String)arrayList.get(i+1));
                }
                else {
                    System.out.println("dis = " + diff_seq.get(i) + " filter " + arrayList.get(i+1));
                }
            }

            return filter;
        }

        public static ArrayList<String> trajDenoise(ArrayList arrayList) {
            ArrayList<String> denoise = new ArrayList<>();
            denoise = (ArrayList<String>) arrayList.clone();

            String[] prev = null;
            String[] curr = null;
            String[] next = null;
            double dis = 0;
            double degree = 0;
            int pointNum = arrayList.size();
            int left = 0;
            int right = 0;

            while (left < pointNum - 2) {
                right = left + 1;
                try {
                    prev = ((String) arrayList.get(left)).split(",");

                    while (right < pointNum - 1) {
                        curr = ((String) arrayList.get(right)).split(",");
                        next = ((String) arrayList.get(right + 1)).split(",");

                        //curr = prev , move the left window one step
                        if (curr[0+3].equals(prev[0+3]) && curr[1+3].equals(prev[1+3])) {
                            break;
                        }

                        //curr = next
                        if (curr[0+3].equals(next[0+3]) && curr[1+3].equals(next[1+3])) {
                            dis = utils.getDistance(Double.parseDouble(curr[0+3]), Double.parseDouble(curr[1+3]),
                                    Double.parseDouble(prev[0+3]), Double.parseDouble(prev[1+3]));
                            if (dis > utils.Max_Distance) {
                                //remove next two, move right window, left window not change
                                System.out.println("dis = " + dis + " denoise " + arrayList.get(right));
                                denoise.remove(arrayList.get(right));
                                denoise.remove(arrayList.get(right+1));
                                right += 2;
                                continue;
                            }
                            else {
                                right += 1;
                                break;
                            }
                        }

                        //prev = next
                        if (prev[0+3].equals(next[0+3]) && prev[1+3].equals(next[1+3])) {
                            dis = utils.getDistance(Double.parseDouble(curr[0+3]), Double.parseDouble(curr[1+3]),
                                    Double.parseDouble(prev[0+3]), Double.parseDouble(prev[1+3]));
                            if (dis > utils.Max_Distance) {
                                //remove next two, move right window, left window not change
                                System.out.println("dis = " + dis + " denoise " + arrayList.get(right));
                                denoise.remove(arrayList.get(right));
                                right += 1;
                                continue;
                            }
                            //valid point, move the slide window
                            break;
                        }

                        degree = utils.getDegree(Double.parseDouble(curr[0+3]), Double.parseDouble(curr[1+3]),
                                Double.parseDouble(prev[0+3]), Double.parseDouble(prev[1+3]),
                                Double.parseDouble(next[0+3]), Double.parseDouble(next[1+3]));

                        if (degree < utils.Degree_Threshold) {
                            System.out.println("degree = " + degree + " denoise " + arrayList.get(right));
                            denoise.remove(arrayList.get(right));
                            right += 1;
                            continue;
                        }
                        //valid point, move the slide window
                        break;
                    }
                    //move window
                    left = right;
                }

                catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }

            try {
                prev = (denoise.get(denoise.size() - 1)).split(",");
                curr = (denoise.get(denoise.size() - 2)).split(",");
                dis = utils.getDistance(Double.parseDouble(curr[0+3]), Double.parseDouble(curr[1+3]),
                        Double.parseDouble(prev[0+3]), Double.parseDouble(prev[1+3]));

                if (dis > utils.Max_Distance) {
                    //remove the last dispersed point
                    System.out.println("dis = " + dis + " denoise " + denoise.get(denoise.size() - 1));
                    denoise.remove(denoise.size() - 1);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            return denoise;
        }

        public static HashMap<Integer, ArrayList<String>> trajAnalysis(ArrayList arrayList) {
            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");

            int i = 0;
            int j = 0;
            int stayIndex = 1;
            int routeIndex = -1;
            String[] left = null;
            String[] right = null;
            String[] prev_right = null;
            int diff = 0;
            double dis;
            boolean token = false;

            ArrayList<String> stayPoint = null;
            ArrayList<String> routePoint = null;
            HashMap<Integer, ArrayList<String>> stayroutePointMap = new HashMap<>();

            int pointNum = arrayList.size();

            while (i < pointNum) {
                j = i + 1;
                token = false;
                try {
                    left = ((String) arrayList.get(i)).split(",");
                    while (j < pointNum) {
                        right = ((String) arrayList.get(j)).split(",");
                        prev_right = ((String) arrayList.get(j - 1)).split(",");

                        // deal the last point
                        dis = utils.getDistance(Double.parseDouble(left[0+3]),
                                Double.parseDouble(left[1+3]),
                                Double.parseDouble(right[0+3]), Double.parseDouble(right[1+3])
                        );

                        if (j == pointNum - 1 && dis <= utils.Distance_Threshold) {//最后两个点的距离小于距离阈值
                            diff = (int) (
                                    (
                                            df.parse(right[2+3]).getTime() - df.parse(left[2+3])
                                                    .getTime()
                                    ) / 1000
                            );

                            if (diff >= utils.Time_Threshold) {//最后两个点的停留时间大于时间阈值15分钟
                                if (stayPoint == null) {
                                    stayPoint = new ArrayList<>();
                                }
                                for (int t = i; t < j + 1; t++) {
                                    stayPoint.add((String)arrayList.get(t));
                                }
                                stayroutePointMap.put(stayIndex, stayPoint);
                                stayPoint = null;
                                token = true;
                            }
                            i = j;
                            break;//将最后两个点放到stayPoint集合中
                        }

                        if (dis > utils.Distance_Threshold) {
                            diff = (int) (
                                    (
                                            df.parse(prev_right[2+3]).getTime() - df.parse(left[2+3])
                                                    .getTime()
                                    ) / 1000
                            );
                            if (diff >= utils.Time_Threshold) {
                                if (stayPoint == null) {
                                    stayPoint = new ArrayList<>();
                                }
                                for (int t = i; t < j; t++) {
                                    stayPoint.add((String)arrayList.get(t));
                                }
                                stayroutePointMap.put(stayIndex, stayPoint);
                                stayIndex ++;
                                stayPoint = null;
                                i = j - 1;
                                token = true;
                            }
                            break;
                        }
                        j += 1;
                    }
                    if (!token) {
                        if (routePoint == null) {
                            routePoint = new ArrayList<>();
                        }
                        routePoint.add((String)arrayList.get(i));
                    }
                    else {
                        if (routePoint != null) {
                            stayroutePointMap.put(routeIndex, routePoint);
                            routePoint = null;
                            routeIndex--;
                        }
                    }

                    i += 1;
                }
                catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }

            if (routePoint != null) {
                stayroutePointMap.put(routeIndex, routePoint);
            }

            return stayroutePointMap;
        }

        public static HashMap<Integer, ArrayList<StayPoint>>trajSegment(HashMap<Integer, ArrayList<String>> stayroutePointMapList) {
            HashMap<Integer, ArrayList<StayPoint>> stayroutePointMap = new HashMap<>();

            for (int key : stayroutePointMapList.keySet()) {
                if (key > 0) {
                    ArrayList<StayPoint> stayPointList = new ArrayList<>();
                    StayPoint sp = getWeightedLocation(stayroutePointMapList.get(key));
                    stayPointList.add(sp);
                    stayroutePointMap.put(key, stayPointList);
                }
            }

            DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");

            for (int key : stayroutePointMapList.keySet()) {
                if (key < 0) {
                    ArrayList<String> obList = stayroutePointMapList.get(key);
                    ArrayList<StayPoint> routePointList = new ArrayList<>();
                    for (String tmp : obList) {
                        double lng = Double.parseDouble(tmp.split(",")[0+3]);
                        double lat = Double.parseDouble(tmp.split(",")[1+3]);
                        long st;
                        try {
                            st = df.parse(tmp.split(",")[2+3]).getTime();
                        }
                        catch (Exception e) {
                            e.printStackTrace();
                            return null;
                        }

                        int radius;
                        if (tmp.split(",")[3+3].equals("outdoor")) {
                            radius = utils.Outdoor_Radius;
                        }
                        else {
                            radius = utils.Indoor_Radius;
                        }

                        StayPoint sp = new StayPoint(new GHPoint(lat, lng), radius, st, 0);
                        routePointList.add(sp);
                    }

                    stayroutePointMap.put(key, routePointList);
                }
            }

            System.out.println("traj segment size = " + stayroutePointMap.size());

            return stayroutePointMap;
        }

        public static ArrayList<Observation> trajReduce(ArrayList<StayPoint> arrayList) {
            int i = 0;
            int j = 0;
            StayPoint temp1 = null;
            StayPoint temp2 = null;
            int diff = 0;
            double lng,lat;
            long tm;

            ArrayList<Observation> samples = new ArrayList<>();
            ArrayList<Double> lngList = new ArrayList<>();
            ArrayList<Double> latList = new ArrayList<>();
            ArrayList<Long> tmList = new ArrayList<>();

            int pointNum = arrayList.size();

            while (i < pointNum) {
                j = i + 1;

                temp1 = arrayList.get(i);
                while (j < pointNum) {
                    temp2 = arrayList.get(j);
                    diff = (int) (temp2.getStarttime() - temp1.getStarttime()) / 1000;

                    if (diff > utils.Reduce_Seconds) {
                        break;
                    }

                    lngList.add(temp2.getPoint().getLon());
                    latList.add(temp2.getPoint().getLat());
                    tmList.add(temp2.getStarttime());
                    j += 1;
                }

                if (lngList.size() > 0) {
                    lngList.add(temp1.getPoint().getLon());
                    latList.add(temp1.getPoint().getLat());
                    tmList.add(temp1.getStarttime());
                    lng = lngList.stream().collect(Collectors.averagingDouble(x -> x));
                    lat = latList.stream().collect(Collectors.averagingDouble(x -> x));
                    tm = (tmList.stream().collect(Collectors.averagingLong(x -> x))).longValue();
                    Observation sample = new Observation(new GHPoint(lat, lng), tm, utils.Outdoor_Radius);
                    samples.add(sample);
                    lngList.clear();
                    latList.clear();
                    tmList.clear();
                }
                else {
                    lng = temp1.getPoint().getLon();
                    lat = temp1.getPoint().getLat();
                    tm = temp1.getStarttime();
                    Observation sample = new Observation(new GHPoint(lat, lng), tm, utils.Outdoor_Radius);
                    samples.add(sample);
                }
                i = j;
            }

            return samples;
        }

        public static List<Observation> trajectoryDPReduce(ArrayList arrayList) {
            ArrayList<Observation> tmpObservation = new ArrayList<>();
            List<Observation> reduced = null;
            String temp = null;

            try {
                for (int i = 0; i < arrayList.size(); i++) {
                    temp = (String) arrayList.get(i);
                    String[] row = temp.split(",");
                    Observation sample = new Observation(
                            new GHPoint(Double.parseDouble(row[1]),
                                    Double.parseDouble(row[0])));
                    tmpObservation.add(sample);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                return null;
            }

            reduced = DouglasPeuckerUtil.DouglasPeucker(tmpObservation, 0.1);
            return reduced;
        }
    }



    public static ArrayList<Observation> multiPointSort(ArrayList<Observation> origList) {
        ArrayList<Observation> distctlist=(ArrayList) origList.stream().distinct().collect(Collectors.toList());

        //not multi polygon
        if (distctlist.size() < 3) {
            return distctlist;
        }

        ArrayList<Observation> sortedPoint = new ArrayList<>();

        double plusX = 0, plusY = 0;
        for (Observation latLng : distctlist) {
            plusX += latLng.getPoint().getLon();
            plusY += latLng.getPoint().getLat();
        }
        Observation center = new Observation(new GHPoint(plusY / distctlist.size(), plusX / distctlist.size()));

        HashMap<Integer, ArrayList<Object>> mapAll = new HashMap<>();
        for (int i = 0; i < distctlist.size(); i++) {
            ArrayList<Object> objList = new ArrayList<>();
            objList.add(distctlist.get(i));
            objList.add(utils.getAngle1(center.getPoint().getLat(), center.getPoint().getLon(),
                    distctlist.get(i).getPoint().getLat(), distctlist.get(i).getPoint().getLon()));
            mapAll.put(i, objList);
        }

        ArrayList<Object> temp = new ArrayList<>();
        int size = mapAll.size();
        for (int i = 0; i < size - 1; i++) {
            for (int j = 0; j < size - 1 - i; j++) {
                if (Double.parseDouble(mapAll.get(j).get(1).toString()) >
                        Double.parseDouble(mapAll.get(j + 1).get(1).toString()))
                {
                    temp = mapAll.get(j);
                    mapAll.put(j, mapAll.get(j + 1));
                    mapAll.put(j + 1, temp);
                }
            }
        }

        for (Integer integer : mapAll.keySet()) {
            if (mapAll.get(integer).get(0) instanceof Observation) {
                sortedPoint.add((Observation) mapAll.get(integer).get(0));
            }
        }

        return sortedPoint;
    }

    public static StayPoint getWeightedLocation(ArrayList<String> origList) {
        ArrayList<Observation> weightedList = new ArrayList<>();

        double lng, lat;
        double radius = 0;
        long st = 0;

        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        ArrayList<Observation> obList = new ArrayList<>();
        try {
            for (String tmp : origList) {
                lng = Double.parseDouble(tmp.split(",")[0+3]);
                lat = Double.parseDouble(tmp.split(",")[1+3]);
                st = df.parse(tmp.split(",")[2+3]).getTime();
                if (tmp.split(",")[3+3].equals("outdoor")) {
                    radius = utils.Outdoor_Radius;
                }
                else {
                    radius = utils.Indoor_Radius;
                }
                obList.add(new Observation(new GHPoint(lat, lng), st, (int) radius));
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

        int ct_indoor = 0;
        // outdoor: distinct ; indoor: not distinct
        for (Observation o : obList) {
            if (o.getRadius() == utils.Outdoor_Radius) {
                if (!weightedList.contains(o)) {
                    weightedList.add(o);
                }
            }
            else {
                weightedList.add(o);
                ct_indoor ++;
            }
        }

        boolean isconfidence = false;
        if (ct_indoor >= utils.Min_Indoor_Count) {
            //indoor centroid
            isconfidence = true;
        }

        int fix = 1000;
        double centroidX = 0, centroidY = 0, plusW = 0;

        for (Observation latLng : weightedList) {
            plusW += fix / latLng.getRadius();
        }

        for (Observation latLng: weightedList) {
            if (isconfidence) {
                if (latLng.getRadius() == utils.Indoor_Radius) {
                    centroidX += latLng.getPoint().getLon();
                    centroidY += latLng.getPoint().getLat();
                }
            }
            else {
                centroidX += latLng.getPoint().getLon() * ((fix / latLng.getRadius()) / plusW);
                centroidY += latLng.getPoint().getLat() * ((fix / latLng.getRadius()) / plusW);
            }
        }

        double dis = 0;

        for (Observation latLng : weightedList) {
            dis += utils.getDistance(latLng.getPoint().getLon(), latLng.getPoint().getLat(), centroidX, centroidY);
        }

        if (weightedList.size() == 1) {
            dis = weightedList.get(0).getRadius();
        }

        dis = dis / weightedList.size();

        if (isconfidence) {
            centroidX = centroidX / ct_indoor;
            centroidY = centroidY / ct_indoor;
            radius = utils.Indoor_Radius;
        }
        else {
            radius = dis / utils.Outdoor_Confidence;
        }

        centroidX = new BigDecimal(centroidX).setScale(utils.Double_Scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        centroidY = new BigDecimal(centroidY).setScale(utils.Double_Scale, BigDecimal.ROUND_HALF_UP).doubleValue();

        drawCircle(centroidX, centroidY, radius);

        System.out.println("centroid coord = " + centroidX + "," + centroidY +
                "  and radius = " + (int)radius +
                "  and confidence = " + isconfidence);


        int stayt = (int)(obList.get(obList.size() - 1).getTime() - obList.get(0).getTime()) / 1000 / 60;
        return new StayPoint(new GHPoint(centroidY, centroidX),
                (int)radius, obList.get(0).getTime(), stayt);
    }


    private static void drawCircle(double centroidX, double centroidY, double radius) {
        double circleX = 0, circleY = 0;
        double dLat = 0, dLng = 0;
        int Multi_Circle = 32;
        double ratio = radius / utils.EARTH_RADIUS;

        System.out.println("***** Stay Circle " + " *****");

        StringBuilder sb_bound = new StringBuilder();
        sb_bound.append("{ \"type\": \"FeatureCollection\", \n");
        sb_bound.append("\"features\": [ \n");
        sb_bound.append("{ \n");
        sb_bound.append("\"type\": \"Feature\", \n");
        sb_bound.append("\"properties\": {}, \n");
        sb_bound.append("\"geometry\": { \n");
        sb_bound.append("\"type\": \"Polygon\", \n");
        sb_bound.append("\"coordinates\":[ [ \n");

        for (int i = Multi_Circle; i > 0; i--) {
            double angle = Math.toRadians(360.0 * i / Multi_Circle);
            dLat = Math.atan(Math.sin(ratio) * Math.cos(angle) / Math.cos(ratio));
            dLng = Math.asin(Math.sin(ratio) * Math.sin(angle) / Math.cos(Math.toRadians(centroidY) + dLat));
            circleX = centroidX + Math.toDegrees(dLng);
            circleY = centroidY + Math.toDegrees(dLat);
            if (i < Multi_Circle) {
                sb_bound.append(", ");
            }
            sb_bound.append('[');
            sb_bound.append(String.format("%.6f", circleX));
            sb_bound.append(',');
            sb_bound.append(String.format("%.6f", circleY));
            sb_bound.append(']');
        }

        sb_bound.append(',');
        sb_bound.append('[');
        dLat = Math.atan(Math.sin(ratio) * Math.cos(0) / Math.cos(ratio));
        dLng = Math.asin(Math.sin(ratio) * Math.sin(0) / Math.cos(Math.toRadians(centroidY) + dLat));
        sb_bound.append(String.format("%.6f", centroidX + Math.toDegrees(dLng)));
        sb_bound.append(',');
        sb_bound.append(String.format("%.6f", centroidY + Math.toDegrees((dLat))));
        sb_bound.append(']');
        sb_bound.append("] ] } } ] \n");
        sb_bound.append("} \n");

        System.out.println(sb_bound.toString());
    }

    public static void generateSpaceTimeData(ArrayList<String> userDataList){
        String relDir =  "";
        String strosm = "./sichuan.osm.pbf";
        String hdfsosm = "hdfs://127.0.0.1:9000/target/sichuan.osm.pbf";
        GraphHopper hopper = createGraphHopperInstance(relDir + hdfsosm);
        generateTrajData(null, userDataList);
        //routing(hopper);

        // release resources to properly shutdown or start a new instance
        hopper.close();
    }

    public static void  generateTrajData(GraphHopper hopper,ArrayList<String> orig) {
        if (null != hopper) {
            PMap hints = new PMap();
            hints.putObject("profile", "car");

            MapMatching mapMatching = new MapMatching(hopper, hints);
        }

        //List<Observation> samples = InputFormatMy.formatFile("D:\\input\\ods\\zhang-1.json");
        ArrayList<String> origList = InputFormatMy.formattxtFile("D:\\input\\ods\\ban-ttt.txt");

        String userId = origList.get(0).split(",")[0];
        System.out.println("original size = " + origList.size());

        ArrayList<String> cleanList = InputFormatMy.trajClean(origList);
        System.out.println("clean size = " + cleanList.size());

        /*ArrayList<String> filterList = InputFormatMy.trajFilter(cleanList);
        System.out.println("filter size = " + filterList.size());*/

        ArrayList<String> denoiseList = InputFormatMy.trajDenoise(cleanList);
        System.out.println("denoise size = " + denoiseList.size());

        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\input\\single\\denoise.txt"))) {
            for (String tmp : denoiseList) {
                bufferedWriter.write(tmp);
                bufferedWriter.write("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        HashMap<Integer, ArrayList<String>> stayroutePointMapList = InputFormatMy.trajAnalysis(denoiseList);  //分段
        HashMap<Integer, ArrayList<StayPoint>> stayroutePointMap = InputFormatMy.trajSegment(stayroutePointMapList); //聚合

        List<List<Observation>> routePointList = new ArrayList<>();//移动点压缩集合
        List<StayPoint> stayPointList = new ArrayList<>();//停留点聚合集合
        for (Integer index : stayroutePointMap.keySet()) {
            if (index<0){
                routePointList.add(InputFormatMy.trajReduce(stayroutePointMap.get(index)));//压缩
            }else{
                stayPointList.addAll(stayroutePointMap.get(index));
            }
        }
        System.out.println("测试");
        /**生成dwd_stay_day数据**/
        List<StayDayData> stayDayList = new ArrayList<>();
        for (int i = 0; i <stayPointList.size(); i++) {
            Integer stayId = i+1;
            StayPoint stayPoint = stayPointList.get(i);
            Double centroidLon = stayPoint.getPoint().getLon();
            Double centroidLat = stayPoint.getPoint().getLat();
            Integer radius = stayPoint.getRadius();
            Integer locType = radius==800?1:0;
            String startTime = utils.milli2Date(stayPointList.get(i).getStarttime());
            Integer stayTime = stayPoint.getStaytime();
            StayDayData stayDayData = new StayDayData(userId, stayId, centroidLon, centroidLat, radius, locType, startTime, stayTime);
            stayDayList.add(stayDayData);
        }
        System.out.println("生成dwd_stay_day数据");
        System.out.println(stayDayList);
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\input\\data\\dwd_stay_day.txt",true))) {
            for (StayDayData data : stayDayList) {
                bufferedWriter.write(data.toString());
                bufferedWriter.write("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        /**生成dwd_stay_detail_day数据**/
        List<StayDetailDayData> stayDetailDayDataList = new ArrayList<>();
        for (Integer index : stayroutePointMapList.keySet()) {
            if (index>0){
                List<String> orgStayDatas = stayroutePointMapList.get(index);
                for (String orgStayData:orgStayDatas) {
                    stayDetailDayDataList.add(new StayDetailDayData(userId,index,orgStayData.split(",")[1],orgStayData.split(",")[2],orgStayData.split(",")[2+3]));
                }
            }
        }
        System.out.println("生成dwd_stay_detail_day数据");
        System.out.println(stayDetailDayDataList);
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\input\\data\\dwd_stay_detail_day.txt",true))) {
            for (StayDetailDayData data : stayDetailDayDataList) {
                bufferedWriter.write(data.toString());
                bufferedWriter.write("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        /**生成dwd_route_detail_day数据**/
        List<RouteDetailDayData> routeDetailDayDataList = new ArrayList<>();
        List<List<RouteDetailDayData>> routeDetailDayDataLists = new ArrayList<>();
        int routeDetailIndex = 1;
        for (List<Observation> observations:routePointList){
            List<RouteDetailDayData> preRouteDetailDayDataList = new ArrayList<>();
            for (Observation observation:observations) {
                long routeTime = observation.getTime();
                String routeTimeStr = utils.milli2Date(routeTime);
                routeDetailDayDataList.add(new RouteDetailDayData(userId,routeDetailIndex,observation.getPoint().getLon(),observation.getPoint().getLat(),routeTimeStr));
                preRouteDetailDayDataList.add(new RouteDetailDayData(userId,routeDetailIndex,observation.getPoint().getLon(),observation.getPoint().getLat(),routeTimeStr));
            }
            routeDetailDayDataLists.add(preRouteDetailDayDataList);
            routeDetailIndex++;
        }

        System.out.println("生成dwd_route_detail_day数据");
        System.out.println(routeDetailDayDataList);
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\input\\data\\dwd_route_detail_day.txt",true))) {
            for (RouteDetailDayData data : routeDetailDayDataList) {
                bufferedWriter.write(data.toString());
                bufferedWriter.write("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        /**生成dwd_route_day数据**/
        List<RouteDayData> routeDayDataList = new ArrayList<>();
        for (List<RouteDetailDayData> routeDetailDayDatas :routeDetailDayDataLists) {
            String startTime = routeDetailDayDatas.get(0).getTimeId();
            String endTime = routeDetailDayDatas.get(routeDetailDayDatas.size()-1).getTimeId();
            Integer startId = 0;
            Integer endId = 0;
            List<Integer> startStayIndexList = new ArrayList<>();
            List<Integer> endStayIndexList = new ArrayList<>();
            for (StayDayData stayDayData:stayDayList) {
                String stayStartTime = stayDayData.getStartTime();
                try {
                    String stayEndTime = utils.milli2Date(new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(stayStartTime).getTime() + 60*1000*stayDayData.getStayTime());
                    if (startTime.compareTo(stayEndTime)>0){
                        startId = stayDayData.getStayId();
                        startStayIndexList.add(startId);
                    }
                    if (endTime.compareTo(stayStartTime)<0){
                        endId = stayDayData.getStayId();
                        endStayIndexList.add(endId);
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            Double distance = 0D;
            if (!startStayIndexList.isEmpty()){
                startId = startStayIndexList.get(startStayIndexList.size()-1);
                distance += utils.getDistance(stayDayList.get(startStayIndexList.size()-1).getCentroidLon(),stayDayList.get(startStayIndexList.size()-1).getCentroidLat(),
                        routeDetailDayDatas.get(0).getLon(),routeDetailDayDatas.get(0).getLat());
                String startStayTime = stayDayList.get(startStayIndexList.size()-1).getStartTime();
                Integer stayStayTime = stayDayList.get(startStayIndexList.size()-1).getStayTime();

                try {
                    startTime = utils.milli2Date(new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(startStayTime).getTime() + 60*1000*stayStayTime);
                } catch (ParseException e) {
                    e.printStackTrace();
                }

            }
            if (!endStayIndexList.isEmpty()){
                endId = endStayIndexList.get(0);
                distance += utils.getDistance(stayDayList.get(endStayIndexList.get(0)-1).getCentroidLon(),stayDayList.get(endStayIndexList.get(0)-1).getCentroidLat(),
                        routeDetailDayDatas.get(routeDetailDayDatas.size()-1).getLon(),routeDetailDayDatas.get(routeDetailDayDatas.size()-1).getLat());
                endTime = stayDayList.get(endStayIndexList.get(0)-1).getStartTime();
            }
            for (int i = 1; i < routeDetailDayDatas.size(); i++) {
                distance += utils.getDistance(routeDetailDayDatas.get(i-1).getLon(),routeDetailDayDatas.get(i-1).getLat(),
                        routeDetailDayDatas.get(i).getLon(),routeDetailDayDatas.get(i).getLat());
            }

            try {
                Double routeTime = (new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(endTime).getTime()-new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(startTime).getTime())/(60*1000.0);
                Double speed = 0D;
                if (Math.abs(routeTime) > 0.001) {
                    speed = distance / 1000 / (routeTime / 60);
                }
                routeDayDataList.add(new RouteDayData(userId,routeDetailDayDatas.get(0).getRouteId(),
                        startId,endId,(int)Math.round(distance),(int)Math.round(speed),0,startTime,(int)Math.round(routeTime)));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生成dwd_route_day数据");
        System.out.println(routeDayDataList);
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:\\input\\data\\dwd_route_day.txt",true))) {
            for (RouteDayData data : routeDayDataList) {
                bufferedWriter.write(data.toString());
                bufferedWriter.write("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

